package com.dc.flow.ui

import android.app.Activity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.dc.flow.ext.processBaseResult
import com.dc.flow.network.BaseResult
import com.dc.flow.state.IBaseState
import com.dc.flow.state.LoadingState
import com.dc.flow.state.PageState
import com.dc.flow.vm.BaseFlowViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch

abstract class BaseFlowFragment<VM: BaseFlowViewModel>: Fragment(), IBaseState {

    protected lateinit var mActivity: Activity

    protected lateinit var mViewModel: VM

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(layoutId(), container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        mActivity = requireActivity()
        mViewModel = createViewModel()
        initView(view)
        initData()
        collectData()
        createObserver()
    }

    private fun collectData() {
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                launch { collectPageState() }
                launch { collectLoadingState() }
            }
        }
    }

    private suspend fun collectPageState() {
        mViewModel.pageState.collect {
            when (it) {
                is PageState.Loading -> showPageLoading(it.loadingMsg)
                is PageState.ShowContent -> showPageContent()
                is PageState.Empty -> showPageEmpty(it.emptyString)
                is PageState.Error -> showPageError(it.exception.message)
            }
        }
    }

    private suspend fun collectLoadingState() {
        mViewModel.loadingState.collect {
            when (it) {
                is LoadingState.Show -> showLoading(it.tipsWord)
                is LoadingState.Hide -> dismissLoading()
            }
        }
    }

    protected fun changeLoadingState(isShowing: Boolean, tipsWord: String = "") {
        mViewModel.changeLoadingState(isShowing, tipsWord)
    }

    protected fun changePageState(pageState: PageState<String>) {
        mViewModel.changePageState(pageState)
    }

    /**
     * 简化BaseResult的处理逻辑。用法：
     * lifecycleScope.launch {
     *      repeatOnLifecycle(Lifecycle.State.STARTED) {
     *          .process(...)
     *      }
     * }
     *
     * @param loadingMsg 加载转圈显示的文本
     * @param errorMsg 请求失败的问本
     * @param loading 加载中的回调
     * @param error 错误回调
     * @param scope 作用域
     * @param success 成功的处理逻辑
     */
    protected fun <T> Flow<BaseResult<T>>.process(
        loadingMsg: String = "",
        errorMsg: String = "",
        loading: (Boolean) -> Unit = { state -> changeLoadingState(state, loadingMsg) },
        error: (Throwable) -> Unit = { error -> processError(error, errorMsg) },
        scope: CoroutineScope,
        success: (T) -> Unit
    ) {
        scope.launch {
            processBaseResult(loading = loading, error = error, success = success)
        }
    }



    /**
     * 没有DataBinding/ViewBinding的布局id
     *
     * @return int
     */
    protected abstract fun layoutId(): Int


    protected abstract fun createViewModel(): VM


    /**
     * 初始化视图
     *
     * @param view View
     */
    protected abstract fun initView(view: View)

    /**
     * 初始化数据
     */
    protected abstract fun initData()

    /**
     * 创建LiveData数据观察者
     */
    protected open fun createObserver() {}

    override fun showPageContent() {

    }

    override fun showPageLoading(msg: String) {

    }

    override fun showPageEmpty(msg: String) {

    }

    override fun showPageError(errorMsg: String?) {

    }

    override fun showLoading(msg: CharSequence) {

    }

    override fun dismissLoading() {

    }

    protected fun processError(error: Throwable, errorMsg: String) {

    }
}